package com.naico.core.service;

import com.naico.core.pojo.*;
import com.naico.util.FinancialUtility;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * User: Dawood Ajaz
 * Date: Dec 27, 2008
 * Time: 10:20:31 PM
 */
public class ActualPaymentService implements CalculatorService {
    public PaymentPlan generate(DebtInformationRequest debtInformationRequest) {

        ActualPaymentPlan actualPaymentPlan = new ActualPaymentPlan();
        List<PaymentSchedule> scheduleList = new ArrayList<PaymentSchedule>();
        List<DebtInformation> debtInfoList = debtInformationRequest.getDebtInformationList();

        for (DebtInformation debtInfo : debtInfoList) {
            PaymentSchedule schedule = generateSchedule(debtInfo);
            scheduleList.add(schedule);
        }

        // setting totals
        actualPaymentPlan.setTotalMonthlyPayment(debtInformationRequest.getTotalActualPayment());
        actualPaymentPlan.setTotalDebtAmount(debtInformationRequest.getTotalBalance());
        actualPaymentPlan.setTotalInterest(debtInformationRequest.getAverageInterest());
        // set actual payment Schedule list.
        actualPaymentPlan.setPaymentSchedules(scheduleList);

        return actualPaymentPlan;
    }

    private PaymentSchedule generateSchedule(DebtInformation debtInformation) {
        PaymentSchedule schedule = new PaymentSchedule();
        List<PaymentScheduleItem> items = new ArrayList<PaymentScheduleItem>();

        String debtName = debtInformation.getDebtName();
        double balance = debtInformation.getBalance();
        double interestRate = debtInformation.getInterestRate();
        double actualPayment = debtInformation.getActualPayment();

        // compute first month payment
        PaymentScheduleItem firstMonthItem = computeFirstMonthPayment(debtInformation);
        items.add(firstMonthItem);

        // compute interest
        double interest = FinancialUtility.computeMonthlyInterest(balance, interestRate);
        // calculate ending balance
        double endingBalance = FinancialUtility.computeEndingBalance(balance, actualPayment, interest);

        // compute remaining months payments
        List<PaymentScheduleItem> remainingMonthsPayment = computeRemainingMonthsPayment(debtName, balance, actualPayment,
                endingBalance, interestRate, debtInformation.getStartDate());

        // adding remaining month payment.
        items.addAll(remainingMonthsPayment);

        // setting items into rollDownSchedule.
        schedule.setPaymentScheduleItems(items);

        return schedule;
    }

    private static PaymentScheduleItem computeFirstMonthPayment(DebtInformation debtInformation) {
        // first rollDownSchedule item.
        PaymentScheduleItem firstMonthPayment = new PaymentScheduleItem();
        firstMonthPayment.setMonth(debtInformation.getStartDate());
        firstMonthPayment.setCreditorName(debtInformation.getDebtName());
        firstMonthPayment.setBeginningBalance(debtInformation.getBalance());
        double interest = FinancialUtility.computeMonthlyInterest(debtInformation.getBalance(), debtInformation.getInterestRate());
        firstMonthPayment.setInterest(interest);
        firstMonthPayment.setPayment(debtInformation.getMinimumPayment());

        // compute ending balance.
        double endingBalance =
                FinancialUtility.computeEndingBalance(debtInformation.getBalance(), debtInformation.getMinimumPayment(), interest);

        firstMonthPayment.setEndingBalance(endingBalance);

        return firstMonthPayment;
    }

    private static List<PaymentScheduleItem> computeRemainingMonthsPayment(String debtName, double balance, double actualPayment,
                                                                           double endingBalance, double interestRate, Date startDate) {

        List<PaymentScheduleItem> remainingMonthsPayment = new ArrayList<PaymentScheduleItem>();

        // set the date.
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        int i = 0;

        // while balance become zero or 600 months i.e. 50 years
        while (endingBalance > 0) {
            balance = endingBalance;
            double interest = FinancialUtility.computeMonthlyInterest(balance, interestRate);
            endingBalance = FinancialUtility.computeEndingBalance(balance, actualPayment, interest);

            // if ending balance goes below zero that means we are over paying
            double overPayment;
            double paymentAmount = actualPayment;
            if (endingBalance < 0) {
                overPayment = -(endingBalance);
                paymentAmount -= overPayment;
                endingBalance = 0.0;
            }

            // increase month by 1
            startCal.add(Calendar.MONTH, 1);

            PaymentScheduleItem item = new PaymentScheduleItem();
            item.setMonth(startCal.getTime());
            item.setCreditorName(debtName);
            item.setBeginningBalance(balance);
            item.setInterest(interest);
            item.setPayment(paymentAmount);
            item.setEndingBalance(endingBalance);

            remainingMonthsPayment.add(item);

            //break the loop if 600 months i.e. 50 years are passed
            if (i++ >= 600)
                break;
        }

        return remainingMonthsPayment;
    }
}
